package com.bluexmicro.ota.ui

import android.bluetooth.BluetoothDevice
import android.bluetooth.le.ScanResult
import android.content.Context
import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.bluexmicro.android.ota.CoroutineOta
import com.bluexmicro.android.ota.OtaManager
import com.bluexmicro.android.ota.OtaParamHelper
import com.bluexmicro.android.ota.entity.OtaState
import com.bluexmicro.android.ota.entity.OtaTask
import com.bluexmicro.module_componment.file.FileResult
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import java.io.File
import java.math.BigInteger

val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

class ExampleViewModel : ViewModel() {

    private val _model = MutableStateFlow(ExampleModel())
    val modelState: StateFlow<ExampleModel> = _model

    private val taskKey = stringSetPreferencesKey("tasks")
    private val fastModeKey = booleanPreferencesKey("fastMode")

    private fun loadTasksFlow(context: Context): Flow<List<FileResult>> =
        context.dataStore.data.map {
            val sets = mutableListOf<FileResult>()
            it[taskKey]?.forEach { formatTask ->
                //"path|hexAddress?"
                val params = formatTask.split('|')
                val file = if (0 in params.indices) {
                    File(params[0])
                } else {
                    null
                }
                val address = if (1 in params.indices) {
                    try {
                        BigInteger(params[1], 16)
                    } catch (e: Exception) {
                        e.printStackTrace()
                        null
                    }
                } else {
                    null
                }
                if (file != null) {
                    sets.add(FileResult(file, address))
                }
            }
            sets
        }

    private fun loadFastModeFlow(context: Context): Flow<Boolean> = context.dataStore.data.map {
        it[fastModeKey] ?: false
    }

    fun loadTasks(context: Context) = viewModelScope.launch {
        loadTasksFlow(context).collect { local ->
            _model.update {
                it.selectedFiles = local
                it.clone()
            }
        }
    }

    fun loadFastMode(context: Context) = viewModelScope.launch {
        loadFastModeFlow(context).collect { fastMode ->
            _model.update {
                it.fastMode = fastMode
                it.clone()
            }
        }
    }

    fun appendFile(context: Context, selectedFile: FileResult) = viewModelScope.launch {
        context.dataStore.edit { settings ->
            val newItem = "${selectedFile.file.path}|${selectedFile.address?.toString(16)}"
            val sets = (settings[taskKey] ?: emptySet()).toMutableSet()
            sets.add(newItem)
            settings[taskKey] = sets
        }
    }

    fun setTarget(result: ScanResult) {
        _model.update {
            it.selectedDevice = result.device
            it.clone()//不考虑性能
        }
    }

    fun removeFile(context: Context, file: FileResult) = viewModelScope.launch {
        val list = modelState.value.selectedFiles.run {
            val newList = toMutableList()
            newList.remove(file)
            newList
        }.map {
            "${it.file.path}|${it.address?.toString(16)}"
        }
        context.dataStore.edit { settings ->
            settings[taskKey] = list.toSet()
        }
    }

    fun toggleFastMode(context: Context) = viewModelScope.launch {
        val newValue = !modelState.value.fastMode
        _model.update {
            it.fastMode = newValue
            it.clone()
        }
        context.dataStore.edit { settings ->
            settings[fastModeKey] = newValue
            if (!newValue && _model.value.selectedFiles.size > 1) {
                val list = listOf(modelState.value.selectedFiles[0]).map {
                    "${it.file.path}|${it.address?.toString(16)}"
                }.toSet()
                settings[taskKey] = list
            }
        }


    }

    private fun setUpdating(value: Boolean) {
        _model.update {
            if (it.updating != value) {
                it.updating = value
                it.clone()
            } else {
                it
            }
        }
    }

    private val _state = MutableSharedFlow<OtaState>()
    val otaStateFlow: SharedFlow<OtaState> = _state

//    fun startOta(context: Context) = viewModelScope.launch {
//        if (modelState.value.updating) return@launch
//        val device = modelState.value.selectedDevice ?: return@launch
//        val tasks = mutableListOf<OtaTask>()
//        for (selectedFile in modelState.value.selectedFiles) {
//            val data = OtaParamHelper.loadBinData(selectedFile.file) ?: continue
//            val task = OtaTask(data, selectedFile.address)
//            tasks.add(task)
//        }
//        if (tasks.isEmpty()) return@launch
//        setUpdating(true)
//        val fastMode = modelState.value.fastMode
//        val otaMgr = OtaManager(context, device)
//        val flow = if (fastMode) {
//            otaMgr.startFastOta(tasks)
//        } else {
//            otaMgr.startOta(tasks[0])
//        }
//        flow.catch {
//            Log.e("Ota", "${it.message}")
//            val state = OtaState()
//            state.startTimeMillis = System.currentTimeMillis()
//            state.endTimeMillis = System.currentTimeMillis()
//            state.errorMessage = it.message
//            _state.emit(state)
//        }.collect {
//            _state.emit(it)
//        }
//        otaMgr.release()
//        setUpdating(false)
//    }

    fun startOta(context: Context) = viewModelScope.launch {
        if (modelState.value.updating) return@launch
        val device = modelState.value.selectedDevice ?: return@launch
        val tasks = mutableListOf<OtaTask>()
        for (selectedFile in modelState.value.selectedFiles) {
            val data = OtaParamHelper.loadBinData(selectedFile.file) ?: continue
            val task = OtaTask(data, selectedFile.address)
            tasks.add(task)
        }
        if (tasks.isEmpty()) return@launch
        setUpdating(true)
        val fastMode = modelState.value.fastMode
        val coroutineOta = CoroutineOta(context, device)

        coroutineOta.startTransaction(fastMode)
        for (withIndex in tasks.withIndex()) {
            var result = true
            val state = OtaState()
            state.startTimeMillis = System.currentTimeMillis()
            state.progress = 0.01f
            state.index = withIndex.index
            coroutineOta.execute(withIndex.value, fastMode).catch {
                result = false
                state.endTimeMillis = System.currentTimeMillis()
                state.errorMessage = it.message
                Log.e("TAG", "catch")
                _state.emit(state)
            }.collect {
                state.progress = it
                _state.emit(state)
            }
            if (result) {
                Log.e("TAG", "finish")
                state.progress = 1f
                state.result = true
                state.endTimeMillis = System.currentTimeMillis()
                _state.emit(state)
            }
        }
        coroutineOta.endTransaction(fastMode)
        setUpdating(false)
    }


}

class ExampleModel {

    var fastMode: Boolean = false
    var selectedFiles: List<FileResult> = emptyList()
    var selectedDevice: BluetoothDevice? = null
    var updating: Boolean = false

    fun clone(): ExampleModel {
        return ExampleModel().also {
            it.fastMode = fastMode
            it.selectedFiles = selectedFiles
            it.selectedDevice = selectedDevice
            it.updating = updating
        }
    }

    override fun toString(): String {
        return "ExampleModel(fastMode=$fastMode, selectedFiles=$selectedFiles, selectedDevice=$selectedDevice, updating=$updating)"
    }


}
